#pragma once

#include <vector>
#include <cstddef>
#include <string>
#include <algorithm>

/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
///
/// @code
/// +-------------------+------------------+------------------+
/// | prependable bytes |  readable bytes  |  writable bytes  |
/// |                   |     (CONTENT)    |                  |
/// +-------------------+------------------+------------------+
/// |                   |                  |                  |
/// 0      <=      readerIndex   <=   writerIndex    <=     size
/// @endcode

// 网络库底层的缓冲区类型定义
class Buffer
{
public:
    static const size_t kCheapPrepend = 8;
    static const size_t kInitialSize = 1024;

    explicit Buffer(size_t initialSize = kInitialSize)
        : buffer_(kCheapPrepend + kInitialSize), readerIndex_(kCheapPrepend), writerIndex_(kCheapPrepend)
    {
    }

    // 状态查询：获取缓冲区当前状态
    // 返回当前可读数据长度
    size_t readableBytes() const
    {
        return writerIndex_ - readerIndex_;
    }
    // 返回当前可写数据长度
    size_t writableBytes() const
    {
        return buffer_.size() - writerIndex_;
    }
    // 返回预留区长度
    size_t prependBytes() const
    {
        return readerIndex_;
    }
    // 返回缓冲区中可读数据的起始地址
    const char *peek() const
    {
        return begin() + readerIndex_;
    }

    // 数据消费：处理可读区数据
    // onMessage string <- buffer
    void retrieve(size_t len)
    {
        // 数据未读完，留待后续处理，仅移动读指针
        if (len < readableBytes())
        {
            readerIndex_ += len; // 应用只读取了可读缓冲区的一部分，就是len字节，还剩下readerIndex_+=len -> writerIndex_
        }
        // 数据读完，调用retrieveAll重置指针
        else //  len == readableBytes()
        {
            retrieveAll();
        }
    }
    void retrieveAll()
    {
        readerIndex_ = writerIndex_ = kCheapPrepend;
    }
    // 把onMeesage函数上报的Buffer数据，转换成string类型的数据返回
    std::string retrieveAllAsString()
    {
        return retrieveAsString(readableBytes()); // 应用可读取数据的长度
    }
    std::string retrieveAsString(size_t len)
    {
        std::string result(peek(), len);
        retrieve(len); // 上面一句已经把缓冲区可读数据读取出来了，这里肯定要对缓冲区进行复位操作
        return result;
    }

    void ensureWriteableBytes(size_t len)
    {
        if (writableBytes() < len)
        {
            makeSpace(len); // 扩容函数
        }
    }
    // 数据写入：把外部数据[data, data+len]内存上的数据，写入缓冲区可写区中
    void append(const char *data, size_t len)
    {
        ensureWriteableBytes(len);
        std::copy(data, data + len, beginWrite());
        writerIndex_ += len;
    }

    char *beginWrite()
    {
        return begin() + writerIndex_;
    }
    const char *beginWrite() const
    {
        return begin() + writerIndex_;
    }

    // 从socket fd读取数据到缓冲区
    ssize_t readFd(int fd, int *saveErrno);
    // 通过socket fd发送数据到缓冲区
    ssize_t writeFd(int fd, int* saveErrno);

private:
    // 扩容
    void makeSpace(size_t len)
    {
        /**
         * kCheapPrepend  |  reader   |  writer  |
         * kCheapPrepend  |            len               |
         */
        if (writableBytes() + prependBytes() < len + kCheapPrepend) // 如果可写数据的空间+可读数据已经读取的空间<len
        {
            buffer_.resize(writerIndex_ + len);
        }
        else // 如果可写数据的空间+可读数据已经读取的空间>len
        {
            size_t readable = readableBytes();
            std::copy(begin() + readerIndex_,
                      begin() + writerIndex_,
                      begin() + kCheapPrepend);
            readerIndex_ = kCheapPrepend;
            writerIndex_ = readerIndex_ + readable;
        }
    }

    // vector底层首元素的地址
    char *begin()
    {
        // it.operator*()
        return &*buffer_.begin();
    }
    const char *begin() const
    {
        return &*buffer_.begin();
    }

    std::vector<char> buffer_;
    size_t readerIndex_;
    size_t writerIndex_;
};